package panel;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import main.MainFrame;
import model.ListItem;

/**
 * An autocompleting custom text box that accepts and executes all commands known by the application engine. The
 * class maintains a floating {@link JWindow} that holds the list of autocomplete entries. This JWindow is popped
 * into display whenever the user starts typing in something. The autocomplete entries are stores in an ArrayList
 * inside the {@link Completer} class. This version of the code uses a linear search to find out the entries that
 * start with whatever the user typed in the textbox.
 * @author Shahriar Haque
 *
 */
public class AddressBar extends JTextField implements ListSelectionListener {

	private static final long serialVersionUID = 1L;
	
	private Completer completer;
	private JList completionList;
	private DefaultListModel completionListModel;
	private JScrollPane listScroller;
	private JWindow listWindow;

	
	public AddressBar(int col){
		super(col);
		
		
		
		completer = new Completer();
		completionListModel = new DefaultListModel();
		completionList = new JList(completionListModel);
		completionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		completionList.addListSelectionListener(this);
		completionList.setCellRenderer(new CustomListCellRenderer());
		
		addMouseListeners();
		listScroller = new JScrollPane(completionList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
													   ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		listWindow = new JWindow();		
		listWindow.getContentPane().add(listScroller);
		
	}
	
	public AddressBar(JList list){
		super();
		
		completer = new Completer();
		completionList = list;
		completionListModel = (DefaultListModel) list.getModel();
		
		completionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		completionList.addListSelectionListener(this);
		addMouseListeners();
		listScroller = new JScrollPane(completionList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
													   ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		listWindow = new JWindow();		
		listWindow.getContentPane().add(listScroller);
	}
	
	
	
	
	private void addMouseListeners() {
		// highlights item on mouse-over
		completionList.addMouseMotionListener(new MouseAdapter() {
			public void mouseMoved(MouseEvent me) {
				Point p = new Point(me.getX(),me.getY());
				completionList.setSelectedIndex(completionList.locationToIndex(p));
			}
		});
		
		// selects an item when clicked
		completionList.addMouseListener(new MouseListener(){

			public void mouseClicked(MouseEvent me) {
				Point p = new Point(me.getX(),me.getY());
				completionList.setSelectedIndex(completionList.locationToIndex(p));
				makeSelection();
				
			}

			public void mouseEntered(MouseEvent me) {}

			public void mouseExited(MouseEvent arg0) {}

			public void mousePressed(MouseEvent arg0) {}

			public void mouseReleased(MouseEvent arg0) {}
			
		});
		
	}

	public void setBounds(int x, int y, int w, int h){
		super.setBounds(x, y, w, h);
		listScroller.setPreferredSize(new Dimension(w,178));
		
	}
	
	
	public void addCompletion(ListItem s) { completer.addCompletion(s); }
	public void removeCompletion(ListItem s) { completer.removeCompletion(s); }
	public void clearCompletions(){ completer.clearCompletions(); }
	
	
	private void makeSelection(){
		listWindow.setVisible(false);
		final ListItem completionString = (ListItem) completionList.getSelectedValue();
		MainFrame.contentPanel.openInCurrentTab(completionString);
		
		Thread worker = new Thread(){
			public void run(){
				setText(completionString.getName());
			}
		};
		
		SwingUtilities.invokeLater(worker);
		
	}
	
	public void valueChanged(ListSelectionEvent e) {
		if(e.getValueIsAdjusting()) return;
		if(completionList.getModel().getSize() == 0) return;
	}
	
	private class Completer implements DocumentListener{
		
		private Pattern pattern;
		private ArrayList<ListItem> completions;
		
		public Completer(){
			completions = new ArrayList<ListItem>();
			getDocument().addDocumentListener(this);
		}
		
		public void addCompletion(ListItem s){
			completions.add(s);
			//buildAndShowPopup();
		}
		
		public void removeCompletion(ListItem s){
			completions.remove(s);
			//buildAndShowPopup();
		}
		
		public void clearCompletions(){
			completions.clear();
			//buildAndShowPopup();
			listWindow.setVisible(false);
		}
		
		private void buildPopup(){
			completionListModel.clear();
			
			Iterator<ListItem> it = completions.iterator();
			
			pattern = Pattern.compile(getText() + ".+");
			
			while(it.hasNext()){
				ListItem completion = it.next();
				Matcher matcher = pattern.matcher(completion.getName());
				
				if(matcher.matches()){
					completionListModel.add(completionListModel.getSize(), completion);
				}
				
			}
			
		}
		
		private void showPopup(){
			if(completionListModel.getSize() == 0){
				listWindow.setVisible(false);
				return;
			}
			
			Point los = getLocationOnScreen();
			int popX = los.x;
			int popY = los.y + getHeight();
			listWindow.setLocation(popX, popY);
			
			
			listWindow.pack();
			listWindow.setVisible(true);
		}
		
		
		private void buildAndShowPopup(){
			if(getText().length() < 1) return;
			buildPopup();
			showPopup();
		}

		public void changedUpdate(DocumentEvent e) {
			
			Thread filler = new Thread(){
				public void run(){
					buildAndShowPopup();
				}
			};
			
			SwingUtilities.invokeLater(filler);
		}

		public void insertUpdate(DocumentEvent e) {
			Thread filler = new Thread(){
				public void run(){
					buildAndShowPopup();
				}
			};
			
			SwingUtilities.invokeLater(filler);
		}

		public void removeUpdate(DocumentEvent e) {
			Thread filler = new Thread(){
				public void run(){
					buildAndShowPopup();
				}
			};
			
			SwingUtilities.invokeLater(filler);
		}
		
	}
	
	
	

}
